/**
 * @author :    hippo
 * @date :      2023-07-30
 * @copyright   Apache 2.0
*/

#ifndef _THREADPOOL_H
#define _THREADPOOL_H

#include <mutex>
#include <condition_variable>
#include <queue>
#include <thread>
#include <functional>
#include <memory>
#include <assert.h>

class ThreadPool {
private:
    struct Pool {
        std::mutex mutex_;
        std::condition_variable cond_;
        bool is_closed_;
        std::queue<std::function<void()>> tasks_;
    };
    std::shared_ptr<Pool> pool_;

public:
    ThreadPool(size_t thread_count = 8): pool_(std::make_shared<Pool>()) {
        assert(thread_count > 0);
        for(size_t i = 0; i < thread_count; i++) {
            std::thread([pool = pool_] {
                std::unique_lock<std::mutex> locker(pool->mutex_);
                while(true) {
                    if(!pool->tasks_.empty()) {
                        auto task = std::move(pool->tasks_.front());
                        pool->tasks_.pop();
                        locker.unlock();
                        task();
                        locker.lock();
                    }
                    else if(pool->is_closed_)
                        break;
                    else
                        pool->cond_.wait(locker);
                }
            }).detach();
        }
    }

    ThreadPool() = delete;

    ThreadPool(ThreadPool&&) = default;

    ~ThreadPool() {
        if(static_cast<bool>(pool_)) {
            {
                std::lock_guard<std::mutex> locker(pool_->mutex_);
                pool_->is_closed_ = true;
            }
            pool_->cond_.notify_all();
        }
    }

    template<class F>
    void AddTask(F&& task) {
        {
            std::lock_guard<std::mutex> locker(pool_->mutex_);
            pool_->tasks_.emplace(std::forward<F>(task));
        }
        pool_->cond_.notify_one();
    }
};

#endif // _THREADPOOL_H